<?php
/**
 * Created by PhpStorm.
 * User: LRChang
 * Date: 2018/6/4
 * Time: 13:55
 * Mail:hjklrchang@qq.com
 */

namespace app\adminapi\model;

use app\adminapi\filter\UserFilter;
use app\base\exception\BaseException;
use app\base\exception\RecordNotFoundException;
use app\base\service\Redis;
use app\base\logic\Sms;
use think\Config;
use think\Db;
use think\Request;
use traits\model\SoftDelete;
class User extends Common
{
    use SoftDelete;
    protected $deleteTime = 'delete_time';

    /**
     * 用户列表
     * @param $where
     * @param $fields
     * @param int $page
     * @param int $pagenum
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function listData($where, $fields, $page = 1, $pagenum = 20) {
        $filter = new UserFilter($fields, $where);
        $condition = $filter->geFiltered();
        $where = $condition['where'];
        $fields = $condition['field'];
        $mark = $filter->getMark();

        $buildQuery = function($query) use ($where) {
            self::authCompanyQuery($query, $where, 'company_id');
        };

        $result = [];
        $result['count'] = $this->where($buildQuery)
            ->count();

        $result['list'] = $this->where($buildQuery)
            ->page($page, $pagenum)
            ->field($fields)
            ->order('user_id desc')
            ->select();

        // 获取每个管理员的角色
        $roleModel = new Role();
        foreach($result['list'] as &$item){
            $role_id = $item->getAttr('role_id');
            if(!$role_id){
                $item['roles'] = [];
                continue;
            }

            $item['roles'] = $roleModel->where("`role_id` IN ($role_id)")->field('role_id, role_name')->select();
        }

        return array_merge($result, $mark);
    }

    /**
     * 新增或者编辑用户
     * @param $paramsc
     * @return $this|false|int
     * @throws BaseException
     * @throws RecordNotFoundException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function createOrUpdate($params) {
        $session = $this->getLoginUser();
        $this->checkAdminAuth($session, self::AUTH_SCOPE_EITHER);

        if(isset($params['user_id'])) {
            $where = ['user_id' => $params['user_id']];
            $buildQuery = function($query) use ($where) {
                self::authCompanyQuery($query, $where, 'company_id', null, true);
            };

            $userRecord = self::get($buildQuery);
            if(!$userRecord) {
                throw new RecordNotFoundException();
            }
        }

        // 单位端管理员新增与编辑的角色所属单位永远是本单位
        if(!self::isSuperAdmin($session)){
            $session['user']['company_id'] && $params['company_id'] = $session['user']['company_id'];

            // 检查角色值是否超出范围
            $map = [];
            $queryRole = function($query) use ($map) {
                // 与company_id组合条件
                $addition = function($query, $field, $value){
                    $query->where($field, $value)->whereOr('role_id', 'IN', Role::PUBLIC_ROLES);
                };

                self::authCompanyQuery($query, $map, 'company_id', $addition);
            };
            $allow_roles = Role::where($queryRole)->column('role_id');
            foreach($allow_roles as &$value){
                $value = strval($value);
            }

            // 合并原有角色
            if(isset($userRecord)){
                $origin_roles = explode(',', $userRecord->getAttr('role_id'));
                $allow_roles = array_merge($allow_roles, $origin_roles);
            }

            $current_roles = explode(',', $params['role_id']);
            foreach($current_roles as $item){
                if(!in_array($item, $allow_roles)) {
                    throw new BaseException("角色[{$item}]不在可授权范围");
                }
            }
        }

        $roles = explode(',', $params['role_id']);
        $roles = array_unique($roles);
        sort($roles);
        $params['role_id'] = implode(',', $roles);

        // 验证单位是否存在
        if($params['company_id'] > 0){
            $this->addRelativeFields( $params, 'app\adminapi\model\Company', $params['company_id'],  [], '关联单位不存在');
        }

        // 检查用户名是否重复
        if(!isset($params['user_id']) || ( isset($userRecord) && $userRecord && $userRecord->getAttr('username') !== $params['username'])){
            $checkName = $this->where('username', $params['username'])->find();
            if($checkName){
                throw new BaseException('该用户名已存在');
            }
        }

        // 检查手机号是否重复
        if(!isset($params['user_id']) || ( isset($userRecord) && $userRecord && $userRecord->getAttr('mobile') !== $params['mobile'])){
            $checkName = $this->where('mobile', $params['mobile'])->find();
            if($checkName){
                throw new BaseException('该手机号用户已存在');
            }
        }

        // 密码为空时不处理
        if(isset($params['password']) && !$params['password']){
            unset($params['password']);
        }

        $originPassword = isset($params['password']) ? $params['password'] : '';
        // 加密密码
        if(isset($params['password'])){
            $params['salt'] = $this->genSalt();
            $params['password'] = CorePwd($params['password'], $params['salt']);
        }

        if(isset($params['user_id'])) {
            return $this->isUpdate(true)->save($params);
        }

        $this->data($params)->save();

        // 用户新增成功后，发送短信通知用户账号密码
        (new Sms())->sendTemplate($params['mobile'], ['name' => $params['username'], 'password' => $originPassword], 'SMS_127157948');

        return $this;
    }

    /**
     * 用户名密码登录
     * @param $userName string 用户名
     * @param $password string 密码
     * @return array
     * @throws BaseException
     * @throws \think\Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function login($userName, $password){
        // 获取用户记录
        $user = $this->where(['username' => $userName])->find();

        if(!$user || CorePwd($password, $user->getAttr('salt')) !== $user->getAttr('password')){
            throw new BaseException('用户名或密码错误');
        }

        if($user->getAttr('state') !== 1){
            throw new BaseException(['msg' => '用户已被禁用', 'errorCode' => 119]);
        }

        $userData = $user->toArray();
        $session = $this->loginAllRoles($userData);

        $this->_log($userData);

        return $session;
    }

    /**
     * 短信验证码登录
     * @param $mobile
     * @param $code
     * @return array
     * @throws BaseException
     * @throws \think\Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function loginBySms($mobile, $code){
        // 获取用户记录
        $user = $this->where(['mobile' => $mobile])->find();

        if(!$user){
            throw new BaseException('该用户不存在');
        }

        if($user->getAttr('state') !== 1){
            throw new BaseException(['msg' => '用户已被禁用', 'errorCode' => 119]);
        }

        // 校验验证码
        $smsModel = new \app\adminapi\model\Sms();
        $record = $smsModel->where(['mobile' => $mobile,'state' => 1, 'scope' => 'login', 'send_time' => ['GT',  time() - 1800]])->order('id desc')->find();
        if(!$record){
            throw new BaseException('短信验证码不正确');
        }

        // 每个验证码允许3次错误
        if($record->getAttr('code') !== $code){
            if($record->getAttr('error_count') >= 3){
                $smsModel->save(['state' => 9], ['mobile' => $mobile, 'scope' => 'login']);
                throw new BaseException('短信验证码不正确，请重新获取登录验证码');
            }

            // 标记错误数
            $record->setInc('error_count', 1);
            throw new BaseException('短信验证码不正确');
        }

        $smsModel->save(['state' => 9], ['mobile' => $mobile, 'scope' => 'login']);

        $userData = $user->toArray();
        $session = $this->loginAllRoles($userData);

        $this->_log($userData);

        return $session;
    }

    /**
     * 发送手机登录验证码
     * @param $mobile
     * @return bool
     * @throws BaseException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function sendSmsLoginCode($mobile){
        // 检查该手机号的用户是否存在以及是否未禁用
        $scope = 'login';
        $user = $this->where(['mobile' => $mobile])->find();
        if(!$user){
            throw new BaseException(['msg' => '该用户不存在']);
        }
        if($user->getAttr('state') !== 1){
            throw new BaseException(['msg' => '用户已被禁用', 'errorCode' => 119]);
        }

        // 检查登录短信发送频率
        $smsModel = new \app\adminapi\model\Sms();
        $records = $smsModel
            ->where(['mobile' => $mobile, 'send_time' => ['GT', strtotime('today')], 'scope' => $scope])
            ->order('id desc')
            ->select();

        $limit = 30;
        if($records && count($records) >= $limit){
            throw new BaseException(['msg' => "今日发送登录短信验证码已达{$limit}条上限"]);
        }

        if($records && (time() - $records[0]->getAttr('send_time')) < 60 ){
            throw new BaseException(['msg' => '验证码已发送，请在'.( 60 - (time() - $records['0']->getAttr('send_time'))) .'秒后重试。']);
        }

        $code = rand(100000,999999);
//        // 发送验证码
        // 队列发送
//        $result = (new Sms())->addToList($mobile, Array('code' => $code), 'SMS_62840083');

        // 立即发送
        $result = (new Sms())->sendTemplate($mobile, ['code' => $code], 'SMS_62840083');

        // 禁用旧的登录验证码
        $smsModel->save(['state' => 9], ['mobile' => $mobile, 'scope' => $scope]);

        // 记录验证码
        $smsModel->isUpdate(false)
            ->save([
                'mobile' => $mobile,
                'code' => $code,
                'send_time' => time(),
                'state' => 1,
                'scope' => 'login',
            ]);

        return true;
    }

    /**
     * 用户退出登录
     * @param $userToken
     * @return bool
     * @throws BaseException
     */
    public function logout($userToken){
        $session = $this->getUserSession($userToken);
        if(!$session){
            throw new BaseException(['msg' => 'user-token 无效', 'errorCode' => 102]);
        }

        $redis = (Redis::getInstance());
        $redis->del('Login:' . $userToken);
        $redis->del('Login:' . $session['user']['user_id']);

        return true;
    }

    /**
     * 验证用户是否登录
     * @return mixed
     * @throws BaseException
     */
    public function checkAuth(){
        $request = Request::instance();
        $userToken = $request->header('user-token');

        // 跳过验证 By 发仔 2018-07-12
        if($userToken == '88888888888888888888888888888888') {

            $session = ['uid'=>1,'username'=>'admin'];

        } else {
            // 后台全部迁移后，去除此判断
            if(!$userToken){
                return null;
            }

            $session = $this->getUserSession($userToken, true);
            $access = strtolower('/' . $request->module() . '/' . $request->controller() . '/' . $request->action());

            if(!$session['user']['is_super_admin'] && !in_array($access, $session['access'])) {
                throw new BaseException('没有操作权限');
            }
        }

        return $session;
    }

    /**
     * 获取当前登录的用户
     * @return mixed|null
     * @throws BaseException
     */
    public function getLoginUser(){
        $userToken = $this->headerToken();

        // 后台全部迁移后，去除此判断
        if(!$userToken){
            return null;
        }

        return $this->getUserSession($userToken, false);
    }

    /**
     * 获取当前登录的单位
     * @return null
     * @throws BaseException
     */
    public function getLoginCompany(){
        $userToken = $this->headerToken();

        // 后台全部迁移后，去除此判断
        if(!$userToken){
            return null;
        }

        $session = $this->getUserSession($userToken, false);

        if(empty($session['company'])){
            throw new BaseException('没有单位操作权限');
        }

        return $session['company'];
    }

    /**
     * 获取用header传过来的user-token
     * @return string
     */
    public function headerToken(){
        return (Request::instance())->header('user-token');
    }

    /**
     * 获取用户session
     * @param string $userToken
     * @param boolean $extendExpires
     * @return mixed
     * @throws BaseException
     */
    public function getUserSession($userToken = '', $extendExpires = false){
        //临时万能TOKEN BY 发仔 2018-08-08
        if($userToken == '88888888888888888888888888888888') {
            return   ["user" => [
                "user_id" => 5,
                "role_id" => "1,2",
                "user_name" => "admin",
                "company_id" => 0,
                "is_super_admin" => true,
                "is_company_admin" => true
            ]];
        }
        // 没有传值则使用header中的值
        if(!$userToken){
            $userToken = $this->headerToken();
        }

        if(!$userToken){
            throw new BaseException('缺少user-token');
        }

        $session = (Redis::getInstance())->get('Login:' . $userToken);
        if(!$session){
            throw new BaseException(['msg' => '登录状态失效，请重新登录', 'errorCode' => 102]);
        }

        // 延长有效期
        if($extendExpires){
            $this->cacheToken($userToken, $session);
        }

        return $session;
    }

    /**
     * 登录该用户下的其他角色
     * @param array $userData
     * @return array
     * @throws \think\Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    protected function loginAllRoles(array $userData){
        // 获取该用户所有权限
        $userData['access'] = ( new Role())->getAccess($userData['role_id']);

        // 获取所属公司
        if($userData['company_id']){
            $companyData = ( new Company() )->where(['company_id' => $userData['company_id']])->field('company_id, user_id, company_name')->find();
            $companyData = $companyData ? $companyData->toArray() : [];
        }else{
            $companyData = [];
        }

        // 设置session
        return $this->setUserSession($userData, $companyData);
    }

    /**
     * 设置用户session
     * @param array $userData
     * @param array $companyData
     * @return array
     */
    protected function setUserSession(array $userData, array $companyData = []){
        $userToken = $this->createToken();

        // 清除之前的token（只能在一个端登录）
        $redis = (Redis::getInstance());
        $oldToken = $redis->get('Login:' . $userData['user_id']);
        if($oldToken){
            $redis->del('Login:' . $oldToken);
        }

        $company = NUll;
        if(!empty($companyData)){
            $company = [
                'company_id' => $companyData['company_id'],
                'user_id' => $companyData['user_id'],
                'company_name' => $companyData['company_name'],
                'is_company_super_admin' => $companyData['user_id'] === $userData['user_id'],
            ];
        }

        $role_ids = explode(',', $userData['role_id']);
        $data = [
            'user' => [
                'user_id' => $userData['user_id'],
                'role_id' => $userData['role_id'],
                'user_name' => $userData['username'],
                'company_id' => $userData['company_id'],
                #'is_super_admin' => in_array(strval(Role::SUPER_ADMIN_ROLE_ID), $role_ids),
                'is_super_admin' => true,
                'is_company_admin' => in_array(strval(Role::COMPANY_ADMIN_ROLE_ID), $role_ids),
        ],
            'company' => $company,
            'access' => $userData['access']
        ];

        $expires = $this->cacheToken($userToken, $data);

        $data['expires'] = $expires;
        $data['user-token'] = $userToken;

        return $data;
    }

    /**
     * 保存用户token及其对应的用户信息
     * @param $userToken
     * @param $userData
     * @return mixed
     */
    protected function cacheToken($userToken, $userData){
        Config::set(include  APP_PATH.'/../config/adminapi/config.php','adminapi');
        $expires = Config::get('adminapi.login_time_expire');
        $redis = (Redis::getInstance());
        // 保存用户信息
        $redis->set('Login:' . $userToken, $userData, $expires);

        // 关联用户token
        $redis->set('Login:' . $userData['user']['user_id'], $userToken, $expires);

        return $expires;
    }

    /**
     * 创建一个token
     * @return string
     */
    protected  function createToken(){
        return md5(uniqid() . time());
    }

    /**
     * 乘客司机新建账号时自动生成用户账号 默认密码 111111
     * @param $phone
     * @param int $from 乘客2 司机5
     * @return mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     * @throws RecordNotFoundException
     */
    public function autoCreate( $phone , $user_id = 0 , $from = 5 ){
        if( $user_id > 0 ){
            $user = $this->where('user_id',$user_id)->whereNull('delete_time')->find();
            if(empty($user)){
                throw new RecordNotFoundException();
            }
            if($user['mobile'] != $phone){
                throw new RecordNotFoundException('用户id和手机号码不相符');
            }
            return $user_id;
        }
        $has = $this->where('mobile',$phone)->whereNull('delete_time')->find();
        if(empty($has)){
            $data['mobile'] = $phone;
            $data['username'] = $phone;
            $data['salt'] = createRandomStr(6);
            $data['password'] = CorePwd('111111',$data['salt']);
            $data['state'] = 1;
            $data['area_id'] = 4;
            $data['role_id'] = $from;
            return $this->insertGetId($data);
        }else{
            $roleArr = explode(',',$has['role_id']);
            if(!in_array( $from ,$roleArr )){
                $roleArr[] = $from;
                $this->save(['role_id'=>implode(',',$roleArr)],['user_id'=>$has['user_id']]);
            }
            return $has['user_id'];
        }
    }

    /**
     * 记录用户登录日志
     * @param array $userData
     * @return int|string
     */
    protected function _log(array $userData) {
        $log = [
            'router' => 'login',
            'user_id' => $userData['user_id'],
            'ip' => request()->ip(),
        ];
        return Db::name('UserLog')->exp('datetime','now()')->insert($log);
    }

    /**
     * 生成盐
     * @return bool|string
     */
    protected function genSalt(){
        return substr(uniqid(), -6);
    }
}